//Promise本质上是个类，执行时传递一个执行器进去，会立即执行
//注意shift和执行器
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        //捕获错误
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING
    value = undefined
    reason = undefined
    successCb = []
    failCb = []
    resolve = value => {
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        //成功回调是否存在，存在就调用
        while (this.successCb.length) {
            //底下已经处理了，不用再传值
            this.successCb.shift()()
        }
    }
    reject = reason => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.reason = reason
        //失败回调是否存在，存在就调用
        while (this.failCb.length) {
            this.failCb.shift()()
        }
    }
    then(successCb, failCb) {
        //可选参数
        successCb = successCb ? successCb :
            value => value
        failCb = failCb ? failCb :
            reason => { throw reason }
        let pm2 = new MyPromise((resolve, reject) => {
            //返回promise，使其支持链式调用
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    //设置异步时间0，只为了异步，然后确保pm2本来还在执行，最后一定能返回
                    try {
                        let x = successCb(this.value)
                        resolvePromise(pm2, x, resolve, reject)
                    } catch (e) {
                        //这里已经是pm2，直接在里面捕获then错误然后reject
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    //设置异步
                    try {
                        let x = failCb(this.reason)
                        resolvePromise(pm2, x, resolve, reject)
                    } catch (e) {
                        //这里已经是pm2，直接在里面捕获then错误然后reject
                        reject(e)
                    }
                }, 0)
            } else {//异步，保存回调，以后用
                this.successCb.push(() => {

                    setTimeout(() => {
                        //设置异步时间0，只为了异步，然后确保pm2本来还在执行，最后一定能返回
                        try {
                            let x = successCb(this.value)
                            resolvePromise(pm2, x, resolve, reject)
                        } catch (e) {
                            //这里已经是pm2，直接在里面捕获then错误然后reject
                            reject(e)
                        }
                    }, 0)

                })
                this.failCb.push(() => {

                    setTimeout(() => {
                        //设置异步
                        try {
                            let x = failCb(this.reason)
                            resolvePromise(pm2, x, resolve, reject)
                        } catch (e) {
                            //这里已经是pm2，直接在里面捕获then错误然后reject
                            reject(e)
                        }
                    }, 0)

                })
            }
        })
        return pm2
    }

    //无论成败，必定执行一次回调，其最终结果可以通过then获取
    finally(cb) {
        return this.then(
            value => {
                return MyPromise.resolve(
                    cb()
                ).then(() => value)
            },
            reason => {
                return MyPromise.resolve(
                    cb()
                ).then(() => { throw reason })
            },
        )
    }

    catch(failCb) {
        return this.then(undefined, failCb)
    }

    static all(array) {
        let result = []
        //异步检测
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addData(k, v) {
                result[k] = v
                index++
                //如若异步未完成，不能直接resolve
                if (index === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let k = array[i]
                if (k instanceof MyPromise) {
                    k.then(value => addData(i, value),
                        reason => reject(reason))
                } else {
                    addData(i, array[i])
                }
            }
        })
    }

    //resolve
    static resolve(value) {
        if (value instanceof MyPromise)
            return value
        return new MyPromise(resolve =>
            resolve(value))
    }
}

function resolvePromise(pm2, x, resolve, reject) {
    if (pm2 === x) {
        //不可以循环调用
        return reject(new TypeError('!'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}

//如若then的失败回调return了结果，那么将作为resolvePromise参数传入，继续处理，甚至可以成功